using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Microsoft.Robotics.Vision.Cameras;
using Microsoft.Robotics.Vision;
using System.Collections.Generic;
using System.Diagnostics;

namespace Test.Robotics.Vision.Interop
{
    [TestClass]
    [DeploymentItem("Microsoft.Robotics.Vision.Interop.dll")]
    public class RGBCameraTest
    {
        private const string CameraFriendlyName = "LifeCam Cinema";

        [Priority(0)]
        [TestMethod]
        [TestCategory("Unit")]
        public void RGBCameraDeviceEnumeration()
        {
            RGBCamera device = DetectCamera();
            if (device == null) 
            {
                Assert.Inconclusive(string.Format("Could not find the camera with friendly name {0}", CameraFriendlyName));
                return;
            }

            List<CaptureFormat> Formats = new List<CaptureFormat>();

            foreach (CaptureFormat captureFormat in device.Formats)
            {
                Formats.Add(captureFormat);
                Debug.WriteLine(
                    "Frame Rate {0} Width {1} Height {2} Subtype {3}",
                    captureFormat.nFrameRate,
                    captureFormat.nWidth,
                    captureFormat.nHeight,
                    captureFormat.subType
                    );
            }

            CaptureFormat Format = Formats[0];

            List<VideoPropertyValue> Properties = new List<VideoPropertyValue>();

            foreach (VideoPropertyValue property in device.Properties)
            {
                Properties.Add(property);
                Debug.WriteLine("Property {0} Value {1}", property.m_Property, property.nValue);                
            }
        }


        [Priority(0)]
        [TestMethod]
        [TestCategory("Unit")]
        public void RGBCameraCaptureFormat()
        {
            RGBCamera device = DetectCamera();
            if (device == null)
            {
                Assert.Inconclusive(string.Format("Could not find the camera with friendly name {0}", CameraFriendlyName));
                return;
            }
            
            CaptureFormat[] captureFormats = new CaptureFormat[3];
            captureFormats[0] = new CaptureFormat() { nFrameRate = 15, nWidth = 640, nHeight = 480, subType = VideoFormat.FromName("YUY2") };
            captureFormats[1] = new CaptureFormat() { nFrameRate = 15, nWidth = 320, nHeight = 240, subType = VideoFormat.FromName("YUY2") };
            captureFormats[2] = new CaptureFormat() { nFrameRate = 30, nWidth = 640, nHeight = 480, subType = VideoFormat.FromName("YUY2") };
            for (int i = 0; i < captureFormats.Length; ++i)
            {
                TestCaptureFormat(device, captureFormats[i]);
            }

            if (device != null)
            {
                device.Shutdown();
            }
        }

        /// <summary>
        /// Tests that the given camera format can be set on the camera
        /// </summary>
        /// <param name="device">The rgb camera device</param>
        /// <param name="desiredCaptureFormat">Desired capture format </param>
        /// <returns></returns>
        private static bool TestCaptureFormat(RGBCamera device, CaptureFormat desiredCaptureFormat)
        {
            CaptureFormat desiredFormat = null;
            bool found = false;
            foreach (CaptureFormat captureFormat in device.Formats)
            {
                Debug.WriteLine(
                    "Frame Rate {0} Width {1} Height {2} Subtype {3}",
                    captureFormat.nFrameRate,
                    captureFormat.nWidth,
                    captureFormat.nHeight,
                    captureFormat.subType
                    );
                if (desiredCaptureFormat.nFrameRate == captureFormat.nFrameRate
                    && desiredCaptureFormat.nWidth == captureFormat.nWidth
                    && desiredCaptureFormat.nHeight == captureFormat.nHeight
                    && desiredCaptureFormat.subType == captureFormat.subType)
                {
                    desiredFormat = captureFormat;
                    found = true;
                    break;
                }
            }

            Assert.IsTrue(found, string.Format(
                "Desired frame rate {0} width {1}, height {2}, subtype {3} combination not found",
                desiredCaptureFormat.nFrameRate,
                desiredCaptureFormat.nWidth,
                desiredCaptureFormat.nHeight,
                desiredCaptureFormat.subType));

            device.CurrentFormat = desiredFormat;
            Assert.IsTrue(
                desiredCaptureFormat.nFrameRate == device.CurrentFormat.nFrameRate
                    && desiredCaptureFormat.nWidth == device.CurrentFormat.nWidth
                    && desiredCaptureFormat.nHeight == device.CurrentFormat.nHeight
                    && desiredCaptureFormat.subType == device.CurrentFormat.subType,
                    "Could not set current format to desired format");
            return found;
        }


        [Priority(0)]
        [TestMethod]
        [TestCategory("Unit")]
        public void RGBCameraVideoProperty()
        {
            RGBCamera device = DetectCamera();
            if (device == null)
            {
                Assert.Inconclusive(string.Format("Could not find the camera with friendly name {0}", CameraFriendlyName));
                return;
            }

            bool found = false;
            string desiredProperty = "Brightness";                      

            foreach (VideoPropertyValue property in device.Properties)
            {             
                Debug.WriteLine("Property {0} Value {1}", property.m_Property.ToString(), property.nValue);
                if (desiredProperty.Equals(property.m_Property.ToString()))
                {
                    found = true;
                    break;
                }
            }

            Assert.IsTrue(found, string.Format(
                "Desired Property {0} not found",
                desiredProperty));

            device.SetProperty(VideoProperty.BacklightCompensation, 0, VideoPropertyFlags.Auto);

            if (device != null)
            {
                device.Shutdown();
            }
        }

        [Priority(0)]
        [TestMethod]
        [TestCategory("Unit")]
        public void RGBCameraVideoFormats()
        {
            Assert.IsTrue(VideoFormat.FromName("AI44") == VideoFormat.FromGuid(VideoFormat.FromName("AI44").Guid));
            Assert.IsTrue(VideoFormat.FromName("ARGB32") == VideoFormat.FromGuid(VideoFormat.FromName("ARGB32").Guid));
            Assert.IsTrue(VideoFormat.FromName("RGB24") == VideoFormat.FromGuid(VideoFormat.FromName("RGB24").Guid));
            Assert.IsTrue(VideoFormat.FromName("RGB565") == VideoFormat.FromGuid(VideoFormat.FromName("RGB565").Guid));
            Assert.IsTrue(VideoFormat.FromName("RGB555") == VideoFormat.FromGuid(VideoFormat.FromName("RGB555").Guid));
            Assert.IsTrue(VideoFormat.FromName("RGB8") == VideoFormat.FromGuid(VideoFormat.FromName("RGB8").Guid));
            Assert.IsTrue(VideoFormat.FromName("YUY2") == VideoFormat.FromGuid(VideoFormat.FromName("YUY2").Guid));
            Assert.IsTrue(VideoFormat.FromName("AYUV") == VideoFormat.FromGuid(VideoFormat.FromName("AYUV").Guid));
            Assert.IsTrue(VideoFormat.FromName("Base") == VideoFormat.FromGuid(VideoFormat.FromName("Base").Guid));
        }

        [Priority(0)]
        [TestMethod]
        [TestCategory("Unit")]
        public void RGBCameraAutoExposure()
        {
            RGBCamera device = DetectCamera();
            if (device == null)
            {
                Assert.Inconclusive(string.Format("Could not find the camera with friendly name {0}", CameraFriendlyName));
                return;
            }
            CaptureFormat captureFormat = new CaptureFormat() { nFrameRate = 15, nWidth = 640, nHeight = 480, subType = VideoFormat.FromName("YUY2") };
            TestCaptureFormat(device, captureFormat);

            Assert.IsTrue(device.SetAutoExposure(true), "Could not set auto exposure to true");   
            Assert.IsTrue(device.SetAutoExposure(false), "Could not set auto exposure to false");

            if (device != null)
            {
                device.Shutdown();
            }
        }

        /// <summary>
        /// Detect whether the camera required for the test is present and return it if found.
        /// </summary>
        private static RGBCamera DetectCamera()
        {
            RGBCamera.Initialize();

            foreach (var device in RGBCamera.AllDevices)
            {
                if (!device.Attach())
                {
                    continue;
                }

                // There are a few different names for the Microsoft LifeCam, but they all contain LifeCam
                if (device.FriendlyName != null && device.FriendlyName.Contains(CameraFriendlyName))
                {
                    Debug.WriteLine("Found Camera of Type {0}", device.FriendlyName);
                    return device;
                }
            }
            return null;
        }
    }
}
